package yxy.apple.nio.client;

import java.net.SocketAddress;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.util.concurrent.DefaultThreadFactory;
import yxy.apple.nio.Initializer;

public class Client {
	private Bootstrap bootstrap = new Bootstrap();

	public static class Connection {
		private ChannelFuture future = null;

		Connection(ChannelFuture future) {
			this.future = future;
		}

		public void disconnect() {
			this.future.channel().close();
		}

		public SocketAddress localAddress() {
			return this.future.channel().localAddress();
		}

		public SocketAddress remoteAddress() {
			return this.future.channel().remoteAddress();
		}
	}

	private SocketAddress remote = null;

	public Client(Initializer initializer, SocketAddress remote, int threads) {
		NioEventLoopGroup group = new NioEventLoopGroup(threads,
				new DefaultThreadFactory(this.getClass().getSimpleName() + "-ClientWorkerThread"));
		this.bootstrap.group(group);
		this.bootstrap.channel(NioSocketChannel.class);
		this.bootstrap.handler(initializer);
		this.remote = remote;
	}

	public Client(Initializer initializer, SocketAddress remote) {
		this(initializer, remote, 0);
	}

	public <T> void setBootstrapOption(ChannelOption<T> option, T value) {
		this.bootstrap.option(option, value);
	}

	public SocketAddress remote() {
		return this.remote;
	}

	public Connection connect() throws Exception {
		return this.connect(null);
	}

	/**
	 * 
	 * @param bind 连接时绑定的本地地址信息
	 * @return
	 * @throws Exception
	 */
	public Connection connect(SocketAddress bind) throws Exception {
		Connection connection = null;
		try {
			ChannelFuture future = this.bootstrap.connect(this.remote, bind).sync();
			connection = new Connection(future);
			future.channel().closeFuture().addListener(new ChannelFutureListener() {
				@Override
				public void operationComplete(ChannelFuture future) throws Exception {
					future.channel().close();
				}
			});
		} catch (Exception e) {
			if (connection != null) {
				connection.disconnect();
			}
			throw e;
		}
		return connection;
	}

	public void shutdown() {
		this.bootstrap.config().group().shutdownGracefully();
	}
}
